package org.dant.ant.extension.xml;

import java.io.*;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.util.DOMElementWriter;
import org.dant.ant.extension.tasks.internal.Endpoint;
import org.dant.ant.extension.tasks.internal.RemoteResponse;

public class XMLResultFormatter implements XMLConstants {

  private Document doc;
  private Element rootElement;
  
  private static DocumentBuilder getDocumentBuilder() {
    try {
      return DocumentBuilderFactory.newInstance().newDocumentBuilder();
    } catch (Exception exc) {
      throw new ExceptionInInitializerError(exc);
    }
  }
  
  public synchronized void writeResults(RemoteResponse res, File basedir) throws BuildException {
    doc = getDocumentBuilder().newDocument();
    switch (res.getTagType()) {
      case RemoteResponse.TASK: {
        rootElement = doc.createElement(TASK);
        break;
      }
      
      case RemoteResponse.TESTCASE: {
        rootElement = doc.createElement(TESTCASE);
        break;
      }
      
      case RemoteResponse.TESTSUITE: {
        rootElement = doc.createElement(TESTSUITE);
        break;
      }
      
      default: {
        System.err.println("Invalid result type detected: " + res);
        return;
      }
    }
        
    rootElement.setAttribute(ATTR_RESULT, printResult(res));
    rootElement.setAttribute(ATTR_NAME, res.getName());
    rootElement.setAttribute(ATTR_PACKAGE, res.getPackageName());

    if (res.getTagType() == RemoteResponse.TASK) {
      if (res.getMultiSited()) {
        Endpoint ep = new Endpoint(res.getFullURL());
        rootElement.setAttribute(ATTR_TASK_NAME, res.getTaskName()+"."+ep.getHostname());
      } else {
        rootElement.setAttribute(ATTR_TASK_NAME, res.getTaskName());
      }
      rootElement.setAttribute(ATTR_TIME, "" + res.getTimes()/1000);
      rootElement.setAttribute(ATTR_HOST, "" + res.getFullURL());
      rootElement.setAttribute(ATTR_DUPLICATE, "" + res.getDuplicate());
      addCommand(rootElement, res);
      addOutput(rootElement, res);
    }
    addDescription(rootElement, res);
    
    String filename = genFileName(res);
    File outfile = new File(basedir, filename);
    OutputStream out = null;
    
    try {
      out = new FileOutputStream(outfile);
    } catch (IOException e) {
      throw new BuildException("Unable to write results file " + outfile.getName() + ": " + outfile);
    }
    
    Writer wri = null;

    try {
      wri = new OutputStreamWriter(out, "UTF8");
      wri.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
      (new DOMElementWriter()).write(rootElement, wri, 0, "  ");
      wri.flush();
    } catch (IOException exc) {
      throw new BuildException("Unable to write log file", exc);
    } finally {
      if (out != System.out && out != System.err) {
        if (wri != null) {
          try {
            wri.close();
          } catch (IOException e) {
            //ignore
          }
        }
      }
    }
  }
  
  private void addDescription(Element componentElement, RemoteResponse res) {
    Element descrElement = doc.createElement(DESCRIPTION);
    
    componentElement.appendChild(descrElement);
    addText(descrElement, res.getResult());
  }
  
  private void addCommand(Element componentElement, RemoteResponse res) {
    Element execElement = doc.createElement(COMMAND);
    
    componentElement.appendChild(execElement);
    addText(execElement, res.getCommand());
  }
  
  private void addOutput(Element componentElement, RemoteResponse res) {
    Element outElement = doc.createElement(SYSTEM_OUT);
    
    componentElement.appendChild(outElement);
    addText(outElement, res.getStdOutBuffer().toString());
    Element errElement = doc.createElement(SYSTEM_ERR);
    
    componentElement.appendChild(errElement);
    addText(errElement, res.getStdErrBuffer().toString());
  }
  
  private void addText(Element elem, String text) {
    Text textNode = doc.createTextNode(text);
    elem.appendChild(textNode);
  }
  
  private String printResult(RemoteResponse res) {
    if (res.isSuccess()) {
      return "PASSED";
    } else {
      return "FAILED";
    }
  }
  
  private String printType(RemoteResponse res) {
    int type = res.getTagType();
    
    switch (type) {
      case RemoteResponse.TASK:
        return "TASK";
        
      case RemoteResponse.TESTSUITE:
        return "SUITE";
        
      case RemoteResponse.TESTCASE:
        return "TESTCASE";
        
      default:
        return "UNKN";
    }
  }
  
  private String genFileName(RemoteResponse res) {
    StringBuffer ret = new StringBuffer();
    
    ret.append(printType(res));
    String pkg = res.getPackageName();
    
    if (pkg != null && !"".equals(pkg)) {
      ret.append("_");
      ret.append(formatName(pkg));
    }
    String name = formatName(res.getName());
    
    if (name != null && !"".equals(name)) {
      ret.append("_");
      ret.append(res.getName());
    }

    String taskName = res.getTaskName();
    if (taskName !=null && !"".equals(taskName)) {
      ret.append("_");
      ret.append(taskName);
      if (res.getMultiSited()) {
        Endpoint ep = new Endpoint(res.getFullURL());
        ret.append("_");
        ret.append(ep.getHostname());
      }
    }
    ret.append(".xml");
    return ret.toString();
  }

  private String formatName(String name) {
    name = name.replace(' ', '.');
    name = name.replace('/', '_');
    name = name.replace('\\', '_');
    return name;
  }
}
